using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.IO;

namespace WebServiceClientGenerator
{
    /// <summary>
    /// Generate source code of a Web Service client that implements a given
    /// interface.
    /// </summary>
    public class SingleClientGenerator : AbstractClientGenerator
    {
        #region Private Fields

        private string _fileName;
        private string _fileExtension;
        private string _classNamespace;
        private string _className;
        private string _xmlNamespace;
        private Type[] _interfaces;

        #endregion

        #region Public Properites

        /// <summary>
        /// Gets and sets the namespace for the generated class.
        /// </summary>
        public string ClassNamespace
        {
            get
            {
                return _classNamespace;
            }
            set
            {
                CodeUtils.CheckEmptyOrNullStringSetting(value);
                _classNamespace = value;
            }
        }

        /// <summary>
        /// Gets and sets the name of the generated class.
        /// </summary>
        public string ClassName
        {
            get
            {
                return _className;
            }
            set
            {
                CodeUtils.CheckEmptyOrNullStringSetting(value);
                _className = value;
            }
        }

        /// <summary>
        /// Gets and sets the file name without extension. Default to be same 
        /// as <see cref="ClassName"/>.
        /// </summary>
        public string FileName
        {
            get { return _fileName ?? _className; }
            set
            {
                _fileName = value;
            }
        }

        /// <summary>
        /// Gets and sets the file extension.
        /// </summary>
        public string FileExtension
        {
            get
            {
                return _fileExtension ?? CodeDomProvider.FileExtension;
            }
            set { _fileExtension = value; }
        }

        /// <summary>
        /// Gets and sets the interface that the generated class will implement.
        /// </summary>
        public Type[] Interfaces
        {
            get
            {
                return _interfaces;
            }
            set
            {
                CodeUtils.CheckEmptyOrNullCollectionSetter(value);
                _interfaces = value;
            }
        }

        /// <summary>
        /// Gets and sets the namespace of the Web Service.
        /// </summary>
        public string XmlNamespace
        {
            get
            {
                return _xmlNamespace;
            }
            set
            {
                CodeUtils.CheckEmptyOrNullStringSetting(value);
                _xmlNamespace = value;
            }
        }

        #endregion

        #region Constructors
        /// <summary>
        /// Construct a new generator instance.
        /// </summary>
        /// <param name="classNamespace">
        /// The namespace for the class being generated.
        /// </param>
        /// <param name="className">
        /// The name of the class.
        /// </param>
        /// <param name="interfaces">
        /// The interfaces to implement.
        /// </param>
        /// <param name="xmlNamespace">
        /// The namespace of the Web Service.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// When any parameter is <see langword="null"/>.
        /// </exception>
        public SingleClientGenerator(
            string classNamespace, string className,
            string xmlNamespace, params Type[] interfaces)
        {
            CodeUtils.CheckStringArgumentEmptyOrNull(classNamespace, "classNamespace");
            CodeUtils.CheckStringArgumentEmptyOrNull(className, "className");
            CodeUtils.CheckStringArgumentEmptyOrNull(xmlNamespace, "xmlNamespace");
            CodeUtils.CheckCollectionArgumentEmptyOrNull(interfaces, "interfaces");

            _classNamespace = classNamespace;
            _className = className;
            _interfaces = interfaces;
            _xmlNamespace = xmlNamespace;
        }
        #endregion

        #region Public Instance Methods

        /// <summary>
        /// Generate the source file in a directory specified by
        /// <paramref name="dirPath"/>.
        /// </summary>
        /// <remarks>
        /// The name of the file is determined by the <see cref="FileName"/>
        /// property and the <see cref="FileExtension"/> property.
        /// </remarks>
        /// <param name="dirPath">
        /// The directory where the source file will be generated.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// When <paramref name="dirPath"/> is <see langword="null"/>.
        /// </exception>
        public void Generate(string dirPath)
        {
            CodeUtils.CheckArgumentNull(dirPath, "dirPath");
            string fullPath = Path.Combine(dirPath, FileName + "." + FileExtension);
            GenerateIfDifferent(fullPath);
        }

        /// <summary>
        /// Write out the code to the <paramref name="writer"/>.
        /// </summary>
        /// <param name="writer">
        /// The writer to where the generated code will be sent.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// When <paramref name="writer"/> is <see langword="null"/>.
        /// </exception>
        public override void Generate(TextWriter writer)
        {
            CodeUtils.CheckArgumentNull(writer, "writer");

            ICodeGenerator generator = CodeDomProvider.CreateGenerator(writer);
            CodeGeneratorOptions options = CodeGeneratorOptions;

            DisableDocumentationWarning(writer, generator, options);

            // generate the code
            generator.GenerateCodeFromNamespace(
                ImplementWebServiceClient(ClassNamespace, ClassName, Interfaces, XmlNamespace),
                writer, options);

            RestoreDocumentationWarning(writer, generator, options);
        }

        #endregion

        #region Private Methods

        // implements the web service client
        private static CodeNamespace ImplementWebServiceClient(
            string classNamespace,
            string className,
            Type[] interfaces,
            string xmlNamespace)
        {
            //Create Namespaces
            CodeNamespace codeNamespace = new CodeNamespace(classNamespace);

            CodeTypeDeclaration codeClass = GenerateClassCode(className, xmlNamespace, interfaces);

            codeNamespace.Types.Add(codeClass);

            return codeNamespace;
        }



        #endregion
    }
}